#include "multigeneregionviswidget.h"

#include "../genosage.h"
#include "../../sagevis.h"
#include "contig.h"
#include "genomeelement.h"
#include "SageVis/VisBaseClasses/visbasecontainer.h"
#include "SageVis/VisBaseClasses/visbasehashcontainer.h"
#include "SageVis/VisBaseClasses/visbaselayout.h"
#include "layoutgenomeelements.h"
#include "../../SageVisGuiElements/lineconnectorelement.h"

MultiGeneRegionVisWidget::MultiGeneRegionVisWidget( QPointer<GenoSage> genoSagePointer, QPointer<SageVis> svp, QGraphicsItem *parent, QString *pathToData ) :
    GenoSageBaseWidget(genoSagePointer, svp, parent)
{    
    //---------SETUP-------------

    //CONTAINERS
    contigContainer = new VisBaseContainer( svp, this, this);
    contigLabelContainer = new VisBaseContainer( svp, this, this);
    genomeElementContainer = new VisBaseHashContainer( svp, this, this );
    lineConnectorContainer = new VisBaseContainer( svp, this, this );

    //DIMENSIONS OF WIDGET
    //  TODO: Should open to a good size depending on size of screen
    setGeometry( QRect(0.0, 0.0, 1000.0, 700.0) );

    //---------FLAGS--------------
    setCursor( Qt::ArrowCursor );//make sure arrow cursor is shown in general
    setFlag( QGraphicsItem::ItemClipsChildrenToShape );//contigs stay in bounds
    setFlag( QGraphicsItem::ItemIsFocusable );//needed for keypress event

    //--------DEAL WITH GETTING DATA FILES AND LOADING DATA
    bool haveData = pathToData; //data path may or may not have been included

    //If data path not included- ask user for data
    if( !haveData )
    {
        //get project data from the user

        qDebug() << "Getting data paths from user" << endl;
        getDataFromUser();
    }

    //Note: these are the only two cases, but must be careful of situations where these don't get 'new-ed'

    //----------LOAD AND PROCESS DATA-----------
    //now that data paths to gff and fasta file have been supplied, begin processing the data
    loadData();

}

//--------------------------------GETTING DATA PATH and LOADING DATA------------------------------

//GET PROJECT INFO FROM THE USER
void MultiGeneRegionVisWidget::getProjectDataFromUser()
{
    //TO DO
}

//GET DATA FROM USER
void MultiGeneRegionVisWidget::getDataFromUser()
{
    getGffFileFromUser();//opens file dialog and returns the path to a signal gff file
    getFastaFileFromUser();//opens file dialog and returns the path to a single fasta file
}

//GET GFF FILE FROM USER
void MultiGeneRegionVisWidget::getGffFileFromUser()
{
    gffFile.clear();//in case another file has been provided
    bool gffFileNameProvided = false;
    QFileDialog *fileDialog = new QFileDialog;
    while( ! gffFileNameProvided ) //while file name not specified
    {
        gffFile = fileDialog->getOpenFileName(0, "Select GFF2 File");//save gff file name provided

        //if no filename provided, or something is wrong with the file- ask user again
        if( gffFile.size() == 0)
        {
            gffFileNameProvided = false;
        }
        else //can exit loop
        {
            gffFileNameProvided = true;
        }
    }
    delete fileDialog;

}

//GET FASTA FILE FROM USER
void MultiGeneRegionVisWidget::getFastaFileFromUser()
{
    fastaFile.clear();//in case another file has been provided
    bool fastaFileNameProvided = false;
    QFileDialog *fileDialog = new QFileDialog;
    while( ! fastaFileNameProvided ) //while file name not specified
    {
        fastaFile = fileDialog->getOpenFileName(0, "Select FASTA File (.faa)");

        //if no filename provided, or something is wrong with the file- ask user again
        if( fastaFile.size() == 0)
        {
            fastaFileNameProvided = false;
            //print an error message and explain that file is needed to proceed
        }
        else //can exit loop
        {
            fastaFileNameProvided = true;
        }
    }
    delete fileDialog;
}


//LAUNCH DATA UPLOADER THREAD
void MultiGeneRegionVisWidget::loadData()
{
    qDebug() << "Loading data" << endl;

    //----- UPLOAD GENOME DATA ------
    //  Data uploaded on a separate thread.
    //  Once upload is complete, 'dataUploadCompleteSlot' is called.
    //  Then data can be stored and graphical objects can be created.
    QThread* thread = new QThread;
    GenomeDataUploader* dataUploader = new GenomeDataUploader(gffFile, fastaFile);
    dataUploader->moveToThread(thread);
    connect(dataUploader, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), dataUploader, SLOT(process()));
    connect(dataUploader, SIGNAL(finished()), thread, SLOT(quit()));
    connect(dataUploader, SIGNAL(finished()), dataUploader, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    //-------------NEED TO DO THIS FOR SIGNALS AND SLOTS WITH MY CUSTOM CLASSES TO WORK---------
    typedef QList< QPointer < GenomeElementDataPacket> > ListOfGenomeElements;
    qRegisterMetaType< QList< QPointer < GenomeElementDataPacket> > >("ListOfGenomeElements");

    typedef QList< QPointer < ContigDataPacket > > ListOfContigDataPackets;
    qRegisterMetaType< QList< QPointer < ContigDataPacket > > >("ListOfContigDataPackets");

    typedef QPointer<GenomeDatabaseInterface> GenomeDatabase;
    qRegisterMetaType< QPointer<GenomeDatabaseInterface> >("GenomeDatabase");

    connect( dataUploader, SIGNAL( dataUploadCompleteSignal( QList< QPointer < GenomeElementDataPacket> >, QList< QPointer < ContigDataPacket > >, QPointer<GenomeDatabaseInterface> )),
             this, SLOT(dataUploadCompleteSlot( QList< QPointer < GenomeElementDataPacket> >, QList< QPointer < ContigDataPacket > >, QPointer<GenomeDatabaseInterface> )));
    //Note:  TO DO:  Track progress
        //connect(dataUploader, SIGNAL(trackProgress(int)), this, SLOT(updateProgressBar(int)));
        //connect(dataUploader, SIGNAL(createdReformattedFileSignal(QString)), this, SLOT(createdReformattedFileSlot(QString)));
        //connect(dataUploader, SIGNAL(finished()), this, SLOT(createContigsSlot()));
    thread->start();
    //Note- don't delete thread pointer now, because needs to run

    //----DONE LAUNCHING DATA UPLOAD THREAD----

}

//COLLECT RESULTS FROM DATA UPLOADER and LAUNCH CONTIG AND GENOME ELEMENT CREATION and LAYOUTer
//slot called when genome data uploader thread is done
//returns a set of genome data elements, contigs and a pointer to the database interface for these data
void MultiGeneRegionVisWidget::dataUploadCompleteSlot( QList< QPointer < GenomeElementDataPacket> > geDataPackets, QList< QPointer < ContigDataPacket > > cDataPackets, QPointer<GenomeDatabaseInterface> dbInterface )
{
    qDebug() << "data upload complete " << endl;
    genomeElementDataPackets.append(geDataPackets); //append this list to the main lis
    contigDataPackets.append( cDataPackets );//append this list to the main list
    databaseInterface = dbInterface;

    //NOTE - later if multiple genome data upload threads are launched, you may need to wait for this to be called a number of times before proceeding
    //instantiate the graphical objects
    createGenomeElementsAndContigs();

    //launch the organizer and layout thread
    launchLayoutThread();

    //connect all genome elements by cluster id
    genomeElementContainer->connectElementsWithSameKey(HOVER_CONNECTION);


    //hover connect genes with the same cluster
    update();
}

//CREATE GRAPHICAL CONTIGS AND GENOME ELEMENTS
//instantiate the genome elements, contigs, and labels
void MultiGeneRegionVisWidget::createGenomeElementsAndContigs()
{
    //for all contigs
    for(int i = 0; i < contigDataPackets.size(); i++)
    {
        Contig* c = new Contig( contigDataPackets[i], sageVisPointer, this, this); //create contig

        SageVisTextLabel* label = new SageVisTextLabel( sageVisPointer, this, this );//label for contig
        label->setGeometry( 0.0, 0.0, 50.0, 20.0 );
        label->setText( contigDataPackets[i]->getContigId() );//label contains contig Id

        //Height-connect labels and contigs
        label->forgeConnection( c, HEIGHT_CONNECTION );
        c->forgeConnection(label, HEIGHT_CONNECTION );

        //Hover-connect labels and contigs
        label->forgeConnection( c, HOVER_CONNECTION );
        c->forgeConnection(label, HOVER_CONNECTION );

        //setup line connection
        label->setLineConnectionType( CENTER_RIGHT_LINE_CONNECTION );
        c->setLineConnectionType( CENTER_LEFT_LINE_CONNECTION );
        lineConnect( label, c );

        //loop through the data packet's genes and add these genes to element list and the contig
        for(int j = 0; j < contigDataPackets[i]->numOfGenomeElements(); j++ )
        {
            GenomeElementDataPacket* geDP = contigDataPackets[i]->getGenomeElementDataPacket(j);
            GenomeElement* g = new GenomeElement( geDP, sageVisPointer, this, c ); //NOTE:  I have made the contig the parent of the new genome element
            c->addGenomeElement(g);

            genomeElements << g; //add genome element to list
            if( geDP->getHasCluster() )//if element has a cluster, add it to hash table
                genomeElementContainer->addElement( g, geDP->getClusterId() );//add genome element to its container- which includes a hashtable so can find genes quickly by clusterId
            else
                genomeElementContainer->addElement( g );
        }

        contigs << c; //add contig to list
        contigContainer->addElement( c );

        contigLabels << label;//add label to list
        contigLabelContainer->addElement( label );
    }
}

void MultiGeneRegionVisWidget::launchLayoutThread()
{
    //------LAYOUT CONTIGS AND CONTIG LABELS------
    int startHeight = 5;

    //Contigs and contig labels start at same height
        //shouldn't need to call for both, since height connected
    contigLabelContainer->setHeightOfElements( startHeight );
    //contigContainer->setHeightOfElements( 10 );//don't need, because of connection

    //----LAYOUT CONTIG LABELS
    VerticalStackLayout* layout2 = new VerticalStackLayout;
    //layout2->setElementsCentered(true);//still determining if needs to be centered or not for correct alignment
    contigLabelContainer->setLayout( layout2 );
    contigLabelContainer->layoutElements();
        //layout pointer will get deleted by container

    //-----LAYOUT CONTIGS
    float shiftX = contigLabelContainer->getMaxElementWidth() + 10;//move contigs over to be 10 px away from longest label

    contigContainer->setXPosOfElements( shiftX );//set initial x position
    contigContainer->setWidthOfElements( (float) boundingRect().width() );//set initial width

    VerticalStackLayout* layout = new VerticalStackLayout;//apply layout
    contigContainer->setLayout(layout);
    contigContainer->layoutElements();
        //layout pointer will get deleted by container

    //-----LAYOUT GENES in contigs and SETUP HEIGHT AND Y POSITION
    for(int i = 0; i < contigs.size(); i++)
    {
        //set height of genome elements
        contigs[i]->setHeightOfElements( startHeight );
        contigs[i]->connectTwoWayElementsToContainer(HEIGHT_CONNECTION);

        //layout genome elements -- non threaded, because otherwise too many threads
        LayoutGenomeElements* l = new LayoutGenomeElements;
        l->setStartNt(0);
        l->setStopNt(100000);
        l->setStartX(0);
        l->setStopX(contigs[i]->geometry().width());
        contigs[i]->setLayout(l);
        contigs[i]->layoutElements();//NonThreaded();
    }
}



//-----------------SIZE AND SHAPE FUNCTIONS----------------------------

void MultiGeneRegionVisWidget::setGeometry(const QRectF &geom)
{
    prepareGeometryChange();
    QGraphicsWidget::setGeometry(geom);
    setPos(geom.topLeft());
}

void MultiGeneRegionVisWidget::setGeometry(qreal x, qreal y, qreal w, qreal h)
{
    setGeometry(QRect(x, y, w, h));
}

QRectF MultiGeneRegionVisWidget::boundingRect() const
{
    return QRectF(QPointF(0,0), geometry().size());
}


QPainterPath MultiGeneRegionVisWidget::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}


//QSizeF MultiGeneRegionVisWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
//{
//    switch (which) {
//    case Qt::MinimumSize:
//        return QSizeF(1000, 600);
//    case Qt::PreferredSize:
//        return QSizeF(1200, 700);
//    case Qt::MaximumSize:
//        return QSizeF(1400, 800);
//    default:
//        break;
//    }
//    return constraint;
//}



//----------------------------DRAW FUNCTIONS---------------------------------
void MultiGeneRegionVisWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    //----prevent compiler warnings by declaring what I am not using---
    Q_UNUSED(widget);
    Q_UNUSED(option);

    //---set color---
    QColor backgroundColor;
    backgroundColor.setRgb(20.0, 20.0, 20.0);
    //if( hovering )
    //    backgroundColor.setRgb(90.0, 90.0, 90.0);

    //---paint background rect---
    painter->setBrush(backgroundColor);
    painter->setPen( Qt::NoPen );
    painter->drawRoundedRect(boundingRect(), 10.0, 10.0);
}


/*
  Scratch:

  SETTING UP A SCROLL AREA AND LINEAR LAYOUT
  Layout worked, scroll area did not
  Abandoned because wanted a dynamic layout
    //--------INITALIZE VARIABLES-------
    contigScrollArea = new QScrollArea;

    contigScrollAreaProxy = new QGraphicsProxyWidget(this);
    contigScrollAreaProxy->setWidget( contigScrollArea );
    contigScrollAreaProxy->setPos(0, 0);
    //contigScrollAreaProxy->setGeometry(QRectF(0, 0, 1000, 800) );//boundingRect());

    contigLayout = new QGraphicsLinearLayout( contigScrollAreaProxy );//this layout manages the contigs
    contigLayout->setOrientation(Qt::Vertical);
    contigScrollAreaProxy->setLayout( contigLayout );



    .......

        Contig* c = new Contig( contigDataPackets[i], sageVisPointer, this, contigScrollAreaProxy); //create contig

        contigLayout->addItem( c );

   ....

  */
